package xyz.ibenben.zhongdian.system.controller;

import net.sf.json.JSONObject;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ByteSource;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.support.CorrelationData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import xyz.ibenben.zhongdian.common.annotation.SystemControllerLog;
import xyz.ibenben.zhongdian.common.configure.AmqpConfig;
import xyz.ibenben.zhongdian.common.constants.Constants;
import xyz.ibenben.zhongdian.common.exception.ExceptionEnum;
import xyz.ibenben.zhongdian.common.exception.MyException;
import xyz.ibenben.zhongdian.common.shiro.PasswordHelper;
import xyz.ibenben.zhongdian.common.util.HttpServletResponseUtil;
import xyz.ibenben.zhongdian.common.util.JsonConvertUtil;
import xyz.ibenben.zhongdian.common.util.RandomUtil;
import xyz.ibenben.zhongdian.system.entity.ChatRecord;
import xyz.ibenben.zhongdian.system.entity.ajax.AjaxJson;
import xyz.ibenben.zhongdian.system.entity.enums.ChatTypeEnum;
import xyz.ibenben.zhongdian.system.entity.sys.SysResources;
import xyz.ibenben.zhongdian.system.entity.sys.SysRole;
import xyz.ibenben.zhongdian.system.entity.sys.SysUser;
import xyz.ibenben.zhongdian.system.entity.sys.SysUserRole;
import xyz.ibenben.zhongdian.system.service.ChatRecordService;
import xyz.ibenben.zhongdian.system.service.impl.EmailSenderService;
import xyz.ibenben.zhongdian.system.service.sys.SysResourcesService;
import xyz.ibenben.zhongdian.system.service.sys.SysRoleService;
import xyz.ibenben.zhongdian.system.service.sys.SysUserRoleService;
import xyz.ibenben.zhongdian.system.service.sys.SysUserService;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;

/**
 * 登录注册控制类
 * 提供了一些基本的服务，如登录、注册、修改密码等方法。
 * spring mvc思想 通过controller来调用service里的方法，
 * service里的方法再通过调用Dao来实现对数据的操作
 * 返回值统统是String类型的返回页面有统一的安排方便代码提取
 *
 * @author chenjian
 * @since 2017年9月27日上午10:57:28
 */
@Controller
@RequestMapping(value="/")  
public class LoginController extends BaseController{
	/* 登录页面 */
	private static final String LOGIN = "login/login";
	/* 跳转首页 */
	private static final String INDEX = "redirect:/index";
	/* 确认邮件页面 */
	private static final String EMAILPAGE = "login/emailActivateFailure";
	/* 忘记密码 */
	private static final String FORGETCODE = "forgetPwdCode";
	/* 确认码时间 */
	private static final String VERIFIED = "verificationCodeTime";
	/* 修改密码页面 */
	private static final String MODIFYPAGE ="login/modifyPassword";

	private final SysResourcesService resourcesService;
	private final SysUserService sysUserService;
	private final SysRoleService sysRoleService;
	private final SysUserRoleService userRoleService;
	private final EmailSenderService emailSenderService;
	private final RabbitTemplate rabbitTemplate;
	private final ChatRecordService chatRecordService;
	
	@Value("${spring.rabbitmq.host}")
	private String host;

	@Autowired
	public LoginController(SysResourcesService resourcesService, SysUserService sysUserService, SysRoleService sysRoleService, SysUserRoleService userRoleService, EmailSenderService emailSenderService, RabbitTemplate rabbitTemplate, ChatRecordService chatRecordService) {
		this.resourcesService = resourcesService;
		this.sysUserService = sysUserService;
		this.sysRoleService = sysRoleService;
		this.userRoleService = userRoleService;
		this.emailSenderService = emailSenderService;
		this.rabbitTemplate = rabbitTemplate;
		this.chatRecordService = chatRecordService;
	}

	/**
	 * 打开登陆页面
	 * @return 页面
	 */
	@RequestMapping(value="/login",method= RequestMethod.GET)
	@SystemControllerLog(description = "打开登陆页面")
	public String login(){
		return LOGIN;
	}

	/**
	 * 登陆
	 * @param user 参数
	 * @param map 参数
	 * @return 页面
	 */
	@RequestMapping(value="/login",method=RequestMethod.POST)
	@SystemControllerLog(description = "登陆")
	public String login(SysUser user, Model map){
		//用户名或密码不能为空！
		if (StringUtils.isEmpty(user.getUsername()) || StringUtils.isEmpty(user.getPassword())) {
			return HttpServletResponseUtil.processModelAndView(map,"用户名或密码不能为空！", LOGIN);
		}
		//账户已锁定！
		SysUser recorduser = sysUserService.selectByUsername(user.getUsername());
		if(recorduser != null && recorduser.getEnable() == 0){
			return HttpServletResponseUtil.processModelAndView(map,"账户已锁定！", LOGIN);
		}
		UsernamePasswordToken token = processToken(user);
		return gotoPage(token, true);
	}

	/**
	 * 处理token
	 * @param user 用户
	 * @return token
	 */
	private UsernamePasswordToken processToken(SysUser user){
		if(StringUtils.isEmpty(user.getRememberMe())){
			return new UsernamePasswordToken(user.getUsername(), user.getPassword());
		}else{
			return new UsernamePasswordToken(user.getUsername(), user.getPassword(), "on".equals(user.getRememberMe()));
		}
	}

	/**
	 * 打开首页
	 * @param map 参数
	 * @param request 请求
	 * @return 页面
	 */
	@RequestMapping(value={"/index", ""}, method= RequestMethod.GET)
	@SystemControllerLog(description = "打开首页")
	public String index(Model map, HttpServletRequest request){
		request.setAttribute("host", host);
		
		//发送登录弹窗
		SysUser user = (SysUser)request.getSession().getAttribute(Constants.SESSION);
		CorrelationData correlationId = new CorrelationData(UUID.randomUUID().toString());
		JSONObject obj = new JSONObject();
		obj.put("type", 1);
		obj.put("username", user.getUsername());
		rabbitTemplate.convertAndSend(AmqpConfig.EXCHANGE, AmqpConfig.ROUTINGKEY, obj.toString(), correlationId);

		//获取聊天记录
		List<ChatRecord> chatList = chatRecordService.selectByLimit(0, 10, null, null, null);
		Collections.reverse(chatList);
		map.addAttribute("chatList", chatList);

		//获取所有用户
		List<SysUser> list = sysUserService.selectAll();
		map.addAttribute("userList", list);
		return "index";
	}

	/**
	 * 发送聊天记录
	 * @param toId 参数
	 * @param type 参数
	 * @param content 实体
	 * @param request 请求
	 * @param response 参数
	 * @return 页面
	 */
	@RequestMapping(value="/sendMessage",method=RequestMethod.POST)
	@SystemControllerLog(description = "发送聊天记录")
	@ResponseBody
	public String sendMessage(Long toId, Integer type, String content, HttpServletRequest request, HttpServletResponse response){
		//没有输入文字
		if(null == content || StringUtils.isEmpty(content)){
			return HttpServletResponseUtil.processErrorStatus(response, "你倒是输入点东西啊");
		}else{
			//组装聊天实体
			ChatRecord record = new ChatRecord();
			record.setToId(toId);
			record.setType(ChatTypeEnum.getByValue(type));
			record.setContent(content);
			SysUser user = (SysUser)request.getSession().getAttribute(Constants.SESSION);
			record.setFromId(user.getId());
			record.setFromUserName(user.getUsername());
			record.setHead(user.getHead());
			record.setFontColor(user.getFontColor());
			record.setBackColor(user.getBackColor());
			if(record.getToId() != null){
				record.setToUserName(sysUserService.selectByKey(record.getToId()).getUsername());
			}
			record.setDelFlag(0);
			record.setSendTime(new Date());
			//保存聊天记录
			chatRecordService.save(record, request, user.getId());
			CorrelationData correlationId = new CorrelationData(UUID.randomUUID().toString());
			JSONObject obj = new JSONObject();
			if(record.getType().ordinal() == 1){
				obj.put("type", 2);
			}else{
				obj.put("type", 3);
			}
			JsonConvertUtil.jsonDateFormatObject(obj, "record", record);
			//发送MQ
			rabbitTemplate.convertAndSend(AmqpConfig.EXCHANGE, AmqpConfig.ROUTINGKEY, obj.toString(), correlationId);
			return HttpServletResponseUtil.processSuccessStatus(response, "");
		}
	}

	/**
	 * 获取聊天记录
	 * @param fromId 参数
	 * @param toId 参数
	 * @param type 参数
	 * @param response 参数
	 * @return 页面
	 */
	@RequestMapping(value="/getMessage",method=RequestMethod.POST)
	@SystemControllerLog(description = "获取聊天记录")
	@ResponseBody
	public String getMessage(Long fromId, Long toId, Integer type, HttpServletResponse response){
		AjaxJson aj = new AjaxJson();
		//获取聊天记录
		List<ChatRecord> list = chatRecordService.selectByLimit(0, 10, fromId, toId, type);
		if(list != null && !list.isEmpty()){
			//顺序倒置一下
			Collections.reverse(list);
			JSONObject obj = new JSONObject();
			JsonConvertUtil.jsonDateFormatArray(obj, "messages", list);
			aj.setObj(obj);
			return HttpServletResponseUtil.processSuccessStatus(response, "");
		}else{
			return HttpServletResponseUtil.processErrorStatus(response, "没有找到聊天记录");
		}
	}

	/**
	 * 获取查找信息
	 * @param search 参数
	 * @return 页面
	 */
	@RequestMapping(value="/search")
	@SystemControllerLog(description = "获取查找信息")
	public String search(String search){
		List<SysResources> list = resourcesService.findByType(1);
		for(SysResources sr: list){
			if(search.equals(sr.getName())){
				return "redirect:" + sr.getResUrl();
			}
		}
		return "index";
	}

	/**
	 * 注册用户信息
	 * @param user 参数
	 * @param request 请求
	 * @param response 参数
	 * @return 页面
	 */
	@RequestMapping(value="/regist",method=RequestMethod.POST)
	@SystemControllerLog(description = "注册用户信息")
	@ResponseBody
	public String regist(SysUser user, HttpServletRequest request, HttpServletResponse response){
		if(null == user || StringUtils.isEmpty(user.getUsername()) || StringUtils.isEmpty(user.getPassword())
				|| StringUtils.isEmpty(user.getEmail())){
			return HttpServletResponseUtil.processErrorStatus(response, "用户信息不完整，请检查");
		}
        String result = processReg(user, response);
        if(Constants.OK.equals(result)){
            return processRegist(user, request, response);
        }
		return result;
	}

	private String processReg(SysUser user, HttpServletResponse response){
        if(!user.getPassword().equals(user.getCpassword())){
            return HttpServletResponseUtil.processErrorStatus(response, "两次密码不一致，请检查");
        }
        if(sysUserService.checkExsit(user.getEmail(), null) != null){
            return HttpServletResponseUtil.processErrorStatus(response, "邮箱已经存在，请更换");
        }
        if(sysUserService.checkExsit(null, user.getUsername()) != null){
            return HttpServletResponseUtil.processErrorStatus(response, "用户名已经存在，请更换");
        }
        return Constants.OK;
    }

	/**
	 * 处理注册信息
	 * @param user 用户
	 * @param request 请求
	 * @param response 响应
	 * @return 页面
	 */
	private String processRegist(SysUser user, HttpServletRequest request, HttpServletResponse response){
		user.setType(false);
		user.setEnable(0);
		user.setFontColor(RandomUtil.getRandomColor());
		user.setBackColor(RandomUtil.getRandomColor());
		sysUserService.save(user, request);
		//发送激活邮件
		String url = "http://" + request.getServerName() + ":" + request.getServerPort();
		String image = url + "/images/a.jpg";
		emailSenderService.sendRegistEmail(user.getId(), user.getUsername(), user.getEmail(), url, image);
		return HttpServletResponseUtil.processSuccessStatus(response, "");
	}

	/**
	 * 邮箱激活
	 * @param userId 用户主键
	 * @param email 邮箱
	 * @param request 请求
	 * @param map 参数
	 * @return 页面
	 */
	@RequestMapping(value="/emailActivate",method=RequestMethod.GET)
	@SystemControllerLog(description = "激活邮箱")
	public String emailActivate(Long userId, String email, HttpServletRequest request, Model map) {
		SysUser user = sysUserService.selectByKey(userId);
		if(user == null) {
			return HttpServletResponseUtil.processModelAndView(map,"用户不存在！", EMAILPAGE);
		}
		if(!user.getEmail().equals(email)) {
			return HttpServletResponseUtil.processModelAndView(map,"用户邮箱不匹配！", EMAILPAGE);
		}
		if(user.getEnable() == 1){
			return HttpServletResponseUtil.processModelAndView(map,"用户已激活账号，不能重复激活！", EMAILPAGE);
		}
		//处理邮件激活
		processEmailActivate(user, request);
		UsernamePasswordToken token = processToken(user);
		return gotoPage(token, false);
	}

	/**
	 * 跳转页面
	 * @param token token
	 * @param isLoginPage 是否是登录页面
	 * @return 页面
	 */
	private String gotoPage(UsernamePasswordToken token, boolean isLoginPage){
		try {
			return processGoToPage(token);
		}catch (LockedAccountException lae) {
			processLockedAccountException(isLoginPage, lae);
		}catch(ExcessiveAttemptsException eae){
			processExcessiveAttemptsException(isLoginPage, eae);
		}catch(AuthenticationException ae){
			processAuthenticationException(isLoginPage, ae);
		} finally {
			token.clear();
		}
		return null;
	}

	private void processLockedAccountException(boolean isLoginPage, LockedAccountException lae){
		throw new MyException(isLoginPage ? ExceptionEnum.LUCKUSEREXCEPTION2 : ExceptionEnum.LUCKUSEREXCEPTION, lae);
	}

	private void processExcessiveAttemptsException(boolean isLoginPage, ExcessiveAttemptsException eae){
		throw new MyException(isLoginPage ? ExceptionEnum.PASSWORDERROREXCEPTION2 : ExceptionEnum.PASSWORDERROREXCEPTION, eae);
	}

	private void processAuthenticationException(boolean isLoginPage, AuthenticationException ae){
		throw new MyException(isLoginPage ? ExceptionEnum.USERORPASSWORDERROREXCEPTION2 : ExceptionEnum.USERORPASSWORDERROREXCEPTION, ae);
	}

	/**
	 * 处理跳转页面
	 * @param token token
	 * @return 页面
	 */
	private String processGoToPage(UsernamePasswordToken token){
		Subject subject = SecurityUtils.getSubject();
		subject.login(token);
		// 获取保存的URL
		return INDEX;
	}

	/**
	 * 处理邮件激活
	 * @param user 用户
	 * @param request 请求
	 */
	private void processEmailActivate(SysUser user, HttpServletRequest request){
		//用户开启使用
		user.setEnable(1);
		sysUserService.updateAll(user, request);
		SysUserRole sur = new SysUserRole();
		sur.setUserId(user.getId());
		SysRole role = sysRoleService.selectByRoleDesc("普通用户");
		sur.setRoleId(role.getId());
		//添加角色为用户
		userRoleService.addUserRole(sur);
	}

	/**
	 * 获取验证码
	 * @param email 邮箱
	 * @param request 请求
	 * @param response 响应
	 * @return 页面
	 */
	@RequestMapping(value="/getVerifyCode",method=RequestMethod.POST)
	@SystemControllerLog(description = "获取验证码")
	@ResponseBody
	public String getVerifyCode(String email, HttpServletRequest request, HttpServletResponse response){
		if(StringUtils.isEmpty(email)){
			return HttpServletResponseUtil.processErrorStatus(response, "您输入的邮箱不能为空！");
		}
		SysUser user = sysUserService.checkExsit(email, null);
		if(user == null) {
			return HttpServletResponseUtil.processErrorStatus(response, "您输入的邮箱不存在！");
		}
		//发送邮箱确认找回密码
		String url = "http://" + request.getServerName() + ":" + request.getServerPort();
		String image = url + "/images/a.jpg";
		Map<String, Object> resultMap = emailSenderService.sendResetPasswordEmail(user.getUsername(), user.getEmail(), url, image);
		request.getSession().setAttribute(FORGETCODE, resultMap.get(FORGETCODE));
		request.getSession().setAttribute(VERIFIED, resultMap.get(VERIFIED));
		return HttpServletResponseUtil.processSuccessStatus(response, "180");
	}

	/**
	 * 找回密码
	 * @param email 邮箱
	 * @param forgetPwdCode 忘记密码代码
	 * @param request 请求
	 * @param response 响应
	 * @return 页面
	 */
	@RequestMapping(value="/pwdBack",method=RequestMethod.POST)
	@SystemControllerLog(description = "找回密码")
	@ResponseBody
	public String pwdBack(String email, String forgetPwdCode, HttpServletRequest request, HttpServletResponse response){
		if(StringUtils.isEmpty(forgetPwdCode)){
			return HttpServletResponseUtil.processErrorStatus(response, "您输入的验证码不能为空！");
		}
		String forgetPwdCodeInSession = (String)request.getSession().getAttribute(FORGETCODE);
		Long verificationCodeTimeInSession = (Long)request.getSession().getAttribute(VERIFIED);
		SysUser user = sysUserService.checkExsit(email, null);
		if(user == null){
			return HttpServletResponseUtil.processErrorStatus(response, "您输入的邮箱不存在！");
		}
		if(System.currentTimeMillis() / 1000 >= verificationCodeTimeInSession){
			return HttpServletResponseUtil.processErrorStatus(response, "您输入的验证码已超时！");
		}
		if(!forgetPwdCode.equals(forgetPwdCodeInSession)){
			return HttpServletResponseUtil.processErrorStatus(response, "您输入的验证码不正确！");
		}
		return processPwdBack(user, request, response);
	}

	/**
	 * 处理密码找回
	 * @param user 用户
	 * @param request 请求
	 * @param response 响应
	 * @return 页面
	 */
	private String processPwdBack(SysUser user, HttpServletRequest request, HttpServletResponse response){
		user.setPassword("123456");
		PasswordHelper p = new PasswordHelper();
		p.encryptPassword(user);
		sysUserService.updateAll(user, request);
		return HttpServletResponseUtil.processSuccessStatus(response, "您的账号密码已经更改为‘123456’，请尽快登录系统后修改密码！");
	}

	/**
	 * 打开修改密码页面
	 * @param map 参数
	 * @param request 请求
	 * @return 页面
	 */
	@RequestMapping(value="/modifyPassword",method=RequestMethod.GET)
	@SystemControllerLog(description = "打开修改密码页面")
	public String modifyPassword(Model map, HttpServletRequest request){
		map.addAttribute("userId", request.getSession().getAttribute(Constants.SESSIONID).toString());
		return MODIFYPAGE;
	}
	
	/**
	 * 提交修改密码页面
	 * @param map 参数
	 * @param user 用户
	 * @param request 请求
	 * @return 页面
	 */
	@RequestMapping(value="/modifyPassword",method=RequestMethod.POST)
	@SystemControllerLog(description = "更新新密码")
	public String modifyPassword(Model map, SysUser user, HttpServletRequest request){
		String result = processModifyPwd(user, map);
		if(Constants.OK.equals(result)){
			SysUser puser = sysUserService.selectByKey(user.getId());
			if(puser == null) {
				return HttpServletResponseUtil.processModelAndView(map,"请重新登录修改密码", MODIFYPAGE);
			}
			String newPassword = new SimpleHash(Constants.ALGORITHMNAME, user.getPassword(),
					ByteSource.Util.bytes(puser.getUsername()), Constants.HASHITERATIONS).toHex();
			if(!puser.getPassword().equals(newPassword)){
				return HttpServletResponseUtil.processModelAndView(map,"原始密码不正确", MODIFYPAGE);
			}
			if(!user.getNpassword().equals(user.getCpassword())) {
				return HttpServletResponseUtil.processModelAndView(map,"两次填写新密码不匹配", MODIFYPAGE);
			}
			return processModifyPassword(user, puser, request);
		}else{
			return result;
		}
	}

	private String processModifyPwd(SysUser user, Model map){
		if(user == null) {
			return HttpServletResponseUtil.processModelAndView(map,"请填写密码及确认密码", MODIFYPAGE);
		}
		if(StringUtils.isEmpty(user.getPassword())){
			return HttpServletResponseUtil.processModelAndView(map,"请填写原始密码", MODIFYPAGE);
		}
		if(StringUtils.isEmpty(user.getNpassword())){
			return HttpServletResponseUtil.processModelAndView(map,"请填写新密码", MODIFYPAGE);
		}
		if(StringUtils.isEmpty(user.getCpassword())){
			return HttpServletResponseUtil.processModelAndView(map,"请填写确认密码", MODIFYPAGE);
		}
		return Constants.OK;
	}

	/**
	 * 处理修改密码
	 * @param user 用户
	 * @param puser 老用户
	 * @param request 请求
	 * @return 页面
	 */
	private String processModifyPassword(SysUser user, SysUser puser, HttpServletRequest request){
		puser.setPassword(user.getNpassword());
		PasswordHelper passwordHelper = new PasswordHelper();
		passwordHelper.encryptPassword(puser);
		sysUserService.updateAll(puser, request);
		return INDEX;
	}

	/**
	 * 403页面
	 * @return 页面
	 */
	@RequestMapping("/403")
	@SystemControllerLog(description = "打开无权限页面")
	public String forbidden(){
		return "error/403";
	}

}
